import { UnauthorizedException } from "@nestjs/common";
import { ClsService } from "nestjs-cls";
import { DataSource, EntitySubscriberInterface, EventSubscriber, InsertEvent, UpdateEvent } from "typeorm";
import { BaseEntity } from "@/database/entity/base/base.entity.";
import { DeletedEntity } from "@/database/entity/base/deleted.entity";

@EventSubscriber()
export class AuditingSubscriber implements EntitySubscriberInterface<BaseEntity> {
    constructor(
        private readonly cls: ClsService,
        dataSource: DataSource,
    ) {
        // 通过 Nest 注入 DataSource 后，把该订阅器注册到 TypeORM，否则构造函数依赖不会被注入
        dataSource.subscribers.push(this);
    }

    listenTo() {
        return BaseEntity; // 只监听继承 BaseEntity 的表
    }

    beforeInsert(event: InsertEvent<BaseEntity>) {
        const user = this.judgmentUser();
        const currentTime = new Date();
        event.entity.createdBy = user.id;
        event.entity.createdAt = currentTime;
        event.entity.updatedBy = user.id;
        event.entity.updatedAt = currentTime;
    }

    beforeUpdate(event: UpdateEvent<DeletedEntity>) {
        if (!event.entity) return;

        // TODO: 批量删除功能
        const user = this.judgmentUser();
        const currentTime = new Date();

        // 软删除时, 二选一设置 deletedAt 或 deletedBy 即可
        if (event.entity.deletedAt || event.entity.deletedBy) {
            const notAllowed = Object.keys(event.entity).filter((key) => !["deletedAt", "deletedBy"].includes(key));
            if (notAllowed.length > 0) {
                // 只允许更新 deletedAt 和 deletedBy 两个字段
                throw new Error(`The ${notAllowed.join(", ")} field is not allowed to be updated on soft deletion`);
            }
            event.entity.deletedBy = user.id;
            event.entity.deletedAt = currentTime;
        } else {
            event.entity.updatedBy = user.id;
            event.entity.updatedAt = currentTime;
        }
    }

    beforeSoftRemove() {
        // 统一使用 update 方法进行软删除
        throw new Error("use the update method for soft deletion");
    }

    /** 校验并返回当前用户, 没有则抛出异常 */
    judgmentUser() {
        const user = this.cls.get("user"); // 从请求上下文拿用户 id
        if (!user || !user.id) {
            throw new UnauthorizedException();
        } else {
            return user;
        }
    }
}
